package com.example.netty.common.server;

import com.example.netty.common.NettyThreadFactory;
import com.example.netty.common.utils.NettyConstants;
import com.example.netty.delimiter.server.DelimiterServerInitializer;
import com.example.netty.fixlength.server.FixLengthServerInitializer;
import com.example.netty.lengthfield.client.LengthFieldClientInitializer;
import com.example.netty.lengthfield.server.LengthFieldServerInitializer;
import com.example.netty.linebase.server.LineBaseServerInitializer;
import com.example.netty.nbcb.server.NormalNBCBServerInitializer;
import com.example.netty.normal.bytebuf.server.NormalByteBufServerInitializer;
import com.example.netty.normal.bytes.server.NormalByteServerInitializer;
import com.example.netty.normal.string.server.NormalStringServerInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * 启动主类
 *
 * @author xin
 * @version Created by xin on 2021/4/30 10:35 上午
 */
@Component
public class NettyServer {

    private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);

    EventLoopGroup boss = null;
    EventLoopGroup worker = null;
    ChannelFuture channelFuture = null;

    @Async
    public void init(String host, int port, String type) {
        // 负责初始化netty服务器，并且开始监听端口的socket请求。
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        // 做是否支持epoll轮询判断以获取更高性能 任务调度框架
        // boss 端口监听线程组
        boss = Epoll.isAvailable() ? new EpollEventLoopGroup(2,
                new NettyThreadFactory("boss")) : new NioEventLoopGroup(2, new NettyThreadFactory("boss"));
        // worker 消息处理线程组
        worker = Epoll.isAvailable() ? new EpollEventLoopGroup(2,
                new NettyThreadFactory("worker")) : new NioEventLoopGroup(2, new NettyThreadFactory("worker"));

        serverBootstrap.group(boss, worker)
                .channel(Epoll.isAvailable() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
                .localAddress(host, port)
                // option 是针对于 boss 线程组
                // 连接保活，默认值为False。启用该功能时，TCP会主动探测空闲连接的有效性。可以将此功能视为TCP的心跳机制，需要注意的是：默认的心跳间隔是7200s即2小时。Netty默认关闭该功能。
                .option(ChannelOption.SO_KEEPALIVE, true)
                // 开启Nagle算法，（尽可能的发送大块数据避免网络中充斥着大量的小数据块）
                .option(ChannelOption.TCP_NODELAY, true)
                // ByteBuf 分配器
                .option(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT)
                .option(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT)
                .option(ChannelOption.SO_REUSEADDR, true);

        // 此处是为了演示用的，初始化不同的 childHandler
        if (NettyConstants.NORMAL_BYTE_BUF.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化normal byte buf server ChannelInitializer ++++++++++++");
            serverBootstrap.childHandler(new NormalByteBufServerInitializer());
        } else if (NettyConstants.NORMAL_BYTE.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化normal byte server ChannelInitializer ++++++++++++");
            serverBootstrap.childHandler(new NormalByteServerInitializer());
        } else if (NettyConstants.NORMAL_STRING.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化normal string server ChannelInitializer ++++++++++++");
            serverBootstrap.childHandler(new NormalStringServerInitializer());
        } else if (NettyConstants.NBCB.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化 粘包、拆包 server ChannelInitializer ++++++++++++");
            serverBootstrap.childHandler(new NormalNBCBServerInitializer());
        } else if (NettyConstants.FIX_LENGTH.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化 粘包、拆包 固定长度解决方案 server ChannelInitializer ++++++++++++");
            serverBootstrap.childHandler(new FixLengthServerInitializer());
        } else if (NettyConstants.LINE_BASE.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化 粘包、拆包 通过在包尾添加回车换行符 \\r\\n 来区分整包消息解决方案 server ChannelInitializer ++++++++++++");
            serverBootstrap.childHandler(new LineBaseServerInitializer());
        } else if (NettyConstants.DELIMITER.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化 粘包、拆包 特殊字符作为分隔符来区分整包消息解决方案 server ChannelInitializer ++++++++++++");
            serverBootstrap.childHandler(new DelimiterServerInitializer());
        }else if (NettyConstants.LENGTH_FIELD.equalsIgnoreCase(type)) {
            logger.debug("+++++++初始化粘包、拆包 指定长度来标识整包消息，通过在包头指定整包长度来约定包长。解决方案 client ChannelInitializer ++++++++++++");
            serverBootstrap.childHandler(new LengthFieldServerInitializer());
        }

        // childOption 是针对于 worker线程组
        // 连接保活，默认值为False。启用该功能时，TCP会主动探测空闲连接的有效性。可以将此功能视为TCP的心跳机制，需要注意的是：默认的心跳间隔是7200s即2小时。Netty默认关闭该功能。
        serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true)
                // 开启Nagle算法，（尽可能的发送大块数据避免网络中充斥着大量的小数据块）
                // TCP参数，立即发送数据，默认值为Ture（Netty默认为True而操作系统默认为False）。该值设置Nagle算法的启用，改算法将小的碎片数据连接成更大的报文来最小化所发送的报文的数量，如果需要发送一些较小的报文，则需要禁用该算法。Netty默认禁用该算法，从而最小化报文传输延时
                .childOption(ChannelOption.TCP_NODELAY, true)
                // ByteBuf 分配器
                .childOption(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT)
                .childOption(ChannelOption.ALLOCATOR, UnpooledByteBufAllocator.DEFAULT)
                .childOption(ChannelOption.SO_REUSEADDR, true);

        // 负责绑定端口，当这个方法执行后，ServerBootstrap就可以接受指定端口上的socket连接了。一个ServerBootstrap可以绑定多个端口。
        try {
            channelFuture = serverBootstrap.bind().sync();
            logger.info("Netty 服务启动成功，端口：{}", channelFuture.channel().localAddress());
        } catch (Exception e) {
            logger.error("启动 Netty 服务时发生异常", e);
        }

        // 监听Channel关闭事件
        ChannelFuture closeFuture = channelFuture.channel().closeFuture();
        try {
            closeFuture.sync();
        } catch (InterruptedException e) {
            logger.error("关闭 Channel 发生异常", e);
        } finally {
            // 关闭线程组
            worker.shutdownGracefully();
            boss.shutdownGracefully();
        }
    }
}
